package com.fattyCorps.game.service;

import cn.hutool.extra.spring.SpringUtil;
import com.fattyCorps.common.mgr.Cfg;
import com.fattyCorps.common.mgr.GlobalConfig;
import com.fattyCorps.common.obj.config.HeroExpMould;
import com.fattyCorps.common.obj.config.HeroMould;
import com.fattyCorps.common.obj.config.SkillMould;
import com.fattyCorps.common.obj.config.StarMould;
import com.fattyCorps.common.obj.dto.DHeroConsume;
import com.fattyCorps.common.obj.dto.DItemConsume;
import com.fattyCorps.common.util.GameCfgUtils;
import com.fattyCorps.game.cache.PlayerCache;
import com.fattyCorps.common.packet.PacketMap;
import com.fattyCorps.game.exception.game.CoinLackErrorException;
import com.fattyCorps.game.exception.game.hero.*;
import com.fattyCorps.game.obj.msg.server.hero.*;
import com.fattyCorps.game.obj.cache.PlayerInfo;
import com.fattyCorps.common.packet.hero.Hero;
import com.fattyCorps.common.packet.hero.component.prop.HeroProp;
import com.fattyCorps.common.packet.hero.component.skill.HeroSkill;
import com.fattyCorps.common.packet.hero.component.skill.component.BirthSkill;
import com.fattyCorps.game.obj.msg.SRet;
import com.fattyCorps.game.service.player.PlayerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HeroService {

    @Autowired
    PlayerCache playerCache;

    @Autowired
    ItemService itemService;

    @Autowired
    GlobalConfig globalConfig;

    @Autowired
    PlayerService playerService;

    @Autowired
    Cfg cfg;

    // 创建英雄
    public void createHero(Integer playerId, Integer typeId) {
        HeroMould heroMould = SpringUtil.getBean(Cfg.class).getCfg(HeroMould.class, typeId);
        if (heroMould == null) {
            throw new HeroMouldIdErrorException();
        }

        Hero hero = Hero.makeHero(heroMould);

        // 增加数据到内存
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        playerInfo.getPlayerData().getHeroes().put(hero);
    }


    // 获取英雄列表
    public SRet getHeroList(Integer playerId) {
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        List<Hero> heroes = playerInfo.getPlayerData().getHeroes().getElems();

//        List<Hero> sortedList = heroes;
        List<Hero> sortedList = heroes.stream().sorted(new Comparator<Hero>() {
            @Override
            public int compare(Hero o1, Hero o2) {
                return (int)(o2.getPropComp().getCombatVal()
                        - o1.getPropComp().getCombatVal());
            }
        }).collect(Collectors.toList());

        List<HeroShort> retList = new ArrayList<>();
        for (Hero hero : sortedList) {
            HeroShort heroShort = new HeroShort();
            heroShort.setId(hero.getId());
            heroShort.setTypeId(hero.getTypeId());
            heroShort.setLv(hero.getLv());
            heroShort.setStar(hero.getStar());
            retList.add(heroShort);
        }

        SGetHeroListMsg sGetHeroListMsg = new SGetHeroListMsg();
        sGetHeroListMsg.setHeroList(retList);
        return SRet.success(sGetHeroListMsg);
    }

    public SRet herDetail(Integer playerId, Integer heroId) {
        SHeroDetailMsg msg = new SHeroDetailMsg();
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        Hero hero = playerInfo.getPlayerData().getHeroes().get(heroId);
        msg.setHeroDetail(getDetail(hero));
        return SRet.success(msg);
    }

    // 升级
    public SRet<SHeroLvUpMsg> lvUp(Integer playerId, Integer heroId) {
//        PacketMap<Hero> heroes = getPacketHero(playerId);
        Hero hero = getHero(playerId, heroId);

        StarMould starMould = StarMould.getCfg(cfg, hero.getStar());
        Integer maxLv = starMould.getMaxLv();
        if (hero.getLv() >= maxLv) {
            throw new HeroLvMaxErrorException();
        }

        // 获取升级经验
        HeroExpMould cfgExp = cfg.getCfg(HeroExpMould.class, hero.getLv());
        // todo:判断道具是否够，执行后续道具逻辑
        Integer needExpItemNum = cfgExp.getExp();
        Integer expItemTypeId = globalConfig.getIntValue(GlobalConfig.EXP_ITEM_TYPE_ID);

        if (!playerService.addCoin(playerId, -cfgExp.getCoin())) {
            throw new CoinLackErrorException();
        }

        boolean b = itemService.delItemByTypeId(playerId, expItemTypeId, needExpItemNum);
        if (!b) {
            throw new HeroExpNotEnoughErrorException();
        }

        hero.lvUp(1);

        SHeroLvUpMsg msg = new SHeroLvUpMsg();
        msg.setHeroDetail(getDetail(hero));
        return SRet.success(msg);
    }

    // 升星
    public SRet<SHeroLvUpMsg> starUp(Integer playerId, Integer heroId, List<Integer> provideHeroes) {
        PacketMap<Hero> heroes = getPacketHero(playerId);
        Hero destHero = heroes.get(heroId);

        Integer maxStar = StarMould.maxStar(cfg);
        if (destHero.getStar() >= maxStar) {
            throw new HeroStarMaxErrorException();
        }

        if (provideHeroes == null || provideHeroes.size() == 0) {
            throw new HeroStarStoneNotEnoughErrorException();
        }

        int nextStar = destHero.getStar() + 1;
        StarMould starMould = StarMould.getCfg(cfg, nextStar);
        List<DItemConsume> dItems = starMould.itemConsume();

        boolean b = itemService.delItemBatch(playerId, dItems);
        if (!b) {
            throw new HeroStarStoneNotEnoughErrorException();
        }

        List<DHeroConsume> dHeroes = starMould.heroConsume();
        List<Boolean> useList = new ArrayList<>(Collections.nCopies(provideHeroes.size(), Boolean.FALSE));
        HeroMould destHeroMould = cfg.getCfg(HeroMould.class, destHero.getTypeId());

        int dNum = 0;
        for (DHeroConsume dHero : dHeroes) {
            dNum += dHero.getNum();
        }

        if (dNum != provideHeroes.size()) {
            throw new HeroStarStoneNotEnoughErrorException();
        }

        // 删除需要消耗的英雄
        for (int i = 0; i < dHeroes.size(); i++) {
            DHeroConsume dHero = dHeroes.get(i);

            switch (dHero.getType()) {
                case Self: {
                    for (int k = 0; k < dHero.getNum(); k++) {
                        boolean find = false;

                        for (int j = 0; j < provideHeroes.size(); j++) {
                            if (useList.get(j)) {
                                continue;
                            }

                            Integer provideHeroId = provideHeroes.get(j);
                            Hero provide = heroes.get(provideHeroId);
                            if (provide.getTypeId().equals(destHero.getTypeId()) && provide.getStar() == dHero.getStar()) {
                                // 找到合适的
                                heroes.remove(provideHeroId);
                                find = true;
                                useList.set(j, Boolean.TRUE);
                                break;
                            }
                        }

                        if (!find) {
                            throw new HeroStarUpMaterialErrorException();
                        }
                    }

                    break;
                }
                case SameCamp: {
                    for (int k = 0; k < dHero.getNum(); k++) {
                        boolean find = false;

                        for (int j = 0; j < provideHeroes.size(); j++) {
                            if (useList.get(j)) {
                                continue;
                            }

                            Integer provideHeroId = provideHeroes.get(j);
                            Hero provide = heroes.get(provideHeroId);
                            HeroMould provideMould = cfg.getCfg(HeroMould.class, provide.getTypeId());

                            if (provideMould.getCamp().equals(destHeroMould.getCamp()) && provide.getStar() == dHero.getStar()) {
                                // 找到合适的
                                heroes.remove(provideHeroId);
                                find = true;
                                useList.set(j, Boolean.TRUE);
                                break;
                            }
                        }

                        if (!find) {
                            throw new HeroStarUpMaterialErrorException();
                        }
                    }

                    break;
                }
                case Arbitrarily: {
                    for (int k = 0; k < dHero.getNum(); k++) {
                        boolean find = false;

                        for (int j = 0; j < provideHeroes.size(); j++) {
                            if (useList.get(j)) {
                                continue;
                            }

                            Integer provideHeroId = provideHeroes.get(j);
                            Hero provide = heroes.get(provideHeroId);

                            if (provide.getStar() == dHero.getStar()) {
                                // 找到合适的
                                heroes.remove(provideHeroId);
                                find = true;
                                useList.set(j, Boolean.TRUE);
                                break;
                            }
                        }

                        if (!find) {
                            throw new HeroStarUpMaterialErrorException();
                        }
                    }

                    break;
                }
            }

        }

        // 修改内存，为hero升星
        destHero.starUp(1);

        SHeroLvUpMsg msg = new SHeroLvUpMsg();
        msg.setHeroDetail(getDetail(destHero));
        return SRet.success(msg);
    }

    public PacketMap<Hero> getPacketHero(Integer playerId) {
        return playerCache.getPlayerInfo(playerId).getPlayerData().getHeroes();
    }

    public Hero getHero(Integer playerId, Integer heroId) {
        return playerCache.getPlayerInfo(playerId).getPlayerData().getHeroes().get(heroId);
    }

    public HeroDetail getDetail(Hero hero) {
        HeroProp propComp = hero.getTrunk().getComponent(HeroProp.class);
        HeroSkill skillComp = hero.getTrunk().getComponent(HeroSkill.class);
        BirthSkill birthSkillComp = skillComp.getTrunk().getComponent(BirthSkill.class);
        List<Integer> birthSkillIds = new ArrayList<>();

        for (SkillMould skill : birthSkillComp.getSkills()) {
            birthSkillIds.add(skill.getId());
        }

        return HeroDetail.builder()
                .id(hero.getId())
                .lv(hero.getLv())
                .star(hero.getStar())
                .typeId(hero.getTypeId())
                .att(propComp.getAtt())
                .def(propComp.getDef())
                .maxHp(propComp.getMaxHp())
                .speed(propComp.getSpeed())
                .skillDamagePer(propComp.getSkillDamagePer())
                .precisePer(propComp.getPrecisePer())
                .parryPer(propComp.getParryPer())
                .criticalPer(propComp.getCriticalPer())
                .criticalDamagePer(propComp.getCriticalDamagePer())
                .armorBreakPer(propComp.getArmorBreakPer())
                .immunityControlPer(propComp.getImmunityControlPer())
                .reduceDamagePer(propComp.getReduceDamagePer())
                .sacredDamagePer(propComp.getSacredDamagePer())
                .reduceCriticalPer(propComp.getReduceCriticalPer())
                .incDamagePer(propComp.getIncDamagePer())
                .incControlPer(propComp.getIncControlPer())
                .heroSkillIds(birthSkillIds)
                .combat(propComp.getCombatVal())
                .build();
    }
}
